#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import abc
import os
import http.client
import json
import ssl
from urllib.request import Request, urlopen
import subprocess

from common.common_models import SubJobDetails
from common.common import output_result_file
from common.const import SubJobStatusEnum, RpcParamKey
from common.exception.common_exception import ErrCodeException
from common.number_const import NumberConst

from ndmp.comm.log import log
from ndmp.comm.utils import report_job_details
from ndmp.comm.const import NDMPCode, NdmpLabel, NdmpCopyStatus, Constant
from ndmp.schemas.ndmp_schemas import ResultInfo
from ndmp.service.client.ndmp_client import NDMPClient, AuthConfig
from common.env_common import get_install_head_path
from common.util import file_utils
from common.security.kmc_util import Kmc
from common.constants.constants import SecurityConstants


class NDMPServiceBase(metaclass=abc.ABCMeta):
    def __init__(self, req_id: str, job_id: str, sub_id: str):
        self.req_id = req_id
        self.job_id = job_id
        self.sub_id = sub_id
        self.speed = 0
        self.lib_path = f"{get_install_head_path()}/DataBackup/ProtectClient/Plugins/\
GeneralDBPlugin/lib/libndmp_client.so"
        self.client_log_path = f"{get_install_head_path()}/DataBackup/ProtectClient/\
ProtectClient-E/slog/Plugins/GeneralDBPlugin"
        self.param = None
        self.ndmp_client = NDMPClient(self.lib_path, self.client_log_path)

    @classmethod
    def is_inner_agent(cls):
        # open agent testcfg.xml
        b_s = ""
        cfg_path = "/opt/DataBackup/ProtectClient/ProtectClient-E/conf/testcfg.tmp"
        if not os.path.exists(cfg_path):
            log.info("file not found, external agent")
            return False
        with open(cfg_path, 'r') as cfg:
            for line in cfg:
                if 'BACKUP_SCENE' in line:
                    b_s = line.split('=')[1].strip()
        if b_s == "1":
            log.info("inner agent")
            return True
        elif b_s == "0":
            log.info("external agent")
            return False
        return False

    @classmethod
    def get_inner_keyfile_pwd(cls):
        pwd = Kmc().decrypt(
                    file_utils.read_file_by_utf_8(SecurityConstants.INTERNAL_KEYFILE_PWD_FILE))
        log.info("decrypt finish")
        return pwd

    def init_label_dict(self):
        label_dict = {
            NdmpCopyStatus.SRC_AUTH_FAILED: {"label": NdmpLabel.NDMP_AUTHENTICATION_FAILED_LABEL,
                                             "param": [self.param.get_src_auth()]},
            NdmpCopyStatus.DST_AUTH_FAILED: {"label": NdmpLabel.NDMP_AUTHENTICATION_FAILED_LABEL,
                                             "param": [self.param.get_dst_auth()]},
            NdmpCopyStatus.DATA_CONNECT_FAILED: {"label": NdmpLabel.NDMP_CREATE_DATA_CONNECT_FAILED_LABEL,
                                                 "param": None},
            NdmpCopyStatus.SEND_DST_RESTORE_FAILED: {"label": NdmpLabel.NDMP_SEND_RECOVER_TO_DST_FAILED_LABEL,
                                                     "param": None},
            NdmpCopyStatus.SEND_SRC_BACKUP_FAILED: {"label": NdmpLabel.NDMP_SEND_BACKUP_TO_SRC_FAILED_LABEL,
                                                    "param": None},
            NdmpCopyStatus.INTERNAL_ERROR: {"label": NdmpLabel.EXECUTE_BACKUP_FAILED_LABEL,
                                            "param": [self.sub_id]}
        }
        return label_dict

    def auth_src_service(self):
        pass

    def auth_dst_service(self):
        pass

    def report_err_result(self, code: int, body_err: int, message: str, exception: dict = None):
        result_info = ResultInfo(code=code, bodyErr=body_err, message=message, exception=exception)
        self.report_result(result_info.dict(by_alias=True))

    def report_result(self, result_info):
        log.info(f"Write result to file, req_id: {self.req_id}")
        try:
            output_result_file(self.req_id, result_info)
        except Exception as e:
            log.error(f"Report job result failed! reason: {str(e)}")

    def authentication(self, auth_conf: AuthConfig):
        err_code = NDMPCode.SUCCESS.value
        msg = "Check connection success!"
        ret = self.ndmp_client.authentication(auth_conf)
        if ret < 0:
            log.error("Authentication failed!")
            err_code = NDMPCode.FAILED.value
            msg = "Authentication failed!"
        return err_code, ret, msg

    def report_speed(self):
        log.info(f"Report speed, job_id: {self.job_id}, speed: {self.speed}")
        report_job_details(self.req_id, self.job_id,
                           SubJobDetails(taskId=self.job_id, subTaskId=self.sub_id,
                                         progress=NumberConst.FIFTY, speed=self.speed,
                                         taskStatus=SubJobStatusEnum.RUNNING.value).dict(by_alias=True))

    # opt: add or del
    def req_opt_ip_rule(self, d_ip: str, opt: str):
        if (len(d_ip) == 0):
            return
        if (not self.is_inner_agent()):
            log.warn("external agent not need add ip rule")
            return
        try:
            cat_file = subprocess.Popen(["cat", Constant.CFG_FILE_PATH], stdout=subprocess.PIPE)
            grep_pod = subprocess.Popen(["grep", "POD_IP"], stdin=cat_file.stdout, stdout=subprocess.PIPE)
            out, err = grep_pod.communicate(timeout=NumberConst.THIRTY)
            proc_out = out.decode().split("=")[1]
            cat_file.stdout.close()
            grep_pod.stdout.close()
        except Exception as ex:
            raise Exception("Timeout") from ex
        op_ip = proc_out.replace('\n', '')
        log.info(f"Enter add ip rule: {d_ip}, server ip:{op_ip}")
        url = f"https://{op_ip}:30173/v1/internal/deviceManager/rest/ip_rule/"
        if opt == 'add':
            url = f"{url}add"
        elif opt == 'del':
            url = f"{url}delete"
        log.info(f"url: {url}")
        data = {
            "task_type": "backup",
            "destination_ip": d_ip
        }
        json_data = json.dumps(data).encode('utf-8')
        cert_path = "/opt/logpath/infrastructure/cert/internal/internal.crt.pem"
        key_path = "/opt/logpath/infrastructure/cert/internal/internal.pem"
        cert_pwd = self.get_inner_keyfile_pwd()
        status_code = -1
        try:
            context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
            context.load_cert_chain(certfile=cert_path, keyfile=key_path, password=cert_pwd)
            request = Request(url, data=json_data, headers={'Content-Type': 'application/json'})
            response = urlopen(request, context=context)
            status_code = response.getcode()
        except FileNotFoundError as e:
            log.error(e, exc_info=True)
        log.info(f"code: {status_code}")
        # 创建SSLContext对象并加载证书
        if status_code == 200:
            log.info(f"add ip rule success. {d_ip}")
        else:
            log.err(f"err add ip rule: {d_ip}")


class LastCopyType:
    last_copy_type_dict = {
        1: [RpcParamKey.FULL_COPY, RpcParamKey.INCREMENT_COPY],
        2: [RpcParamKey.FULL_COPY],
        3: [RpcParamKey.LOG_COPY],
        4: [RpcParamKey.FULL_COPY, RpcParamKey.INCREMENT_COPY, RpcParamKey.LOG_COPY]
    }